import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {
  ActivityIndicator, Animated,
  Dimensions,
  FlatList,
  LayoutAnimation,
  Platform,
  RefreshControl,
  ScrollView,
  StyleSheet,
  Text,
  TouchableWithoutFeedback,
  View,
} from 'react-native';
import RefreshableScrollView from './refreshableScrollView';
import SmartRefreshHeader from "./SmartRefreshHeader";
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
let { width, height } = Dimensions.get('window');
const PaginationStatus = {
  firstLoad: 0,
  waiting: 1,
  loading: 2,
  loadFailed: 3,
  allLoaded: 4,
};

class ListEmptyComponent extends React.PureComponent {

  static propTypes = {
    emptyTextStyle: PropTypes.object,
    emptyText: PropTypes.string,
  };

  render() {
    return (
        <View style={styles.listEmpty}>
          <Text style={this.props.emptyTextStyle}>{this.props.emptyText}</Text>
        </View>
    );
  }
}


export default class UltimateListView extends Component {
  static defaultProps = {
    initialNumToRender: 10,
    horizontal: false,
    headerCount: 0,
    headerAtIndex: null,
    firstLoader: true,
    scrollEnabled: true,
    onFetch: null,
    // Custom View
    header: null,
    paginationAllLoadedView: null,
    paginationWaitingView: null,
    //emptyView: null,
    separator: null,

    // Refreshable
    refreshable: true,
    refreshableMode: 'basic', // basic or advanced

    // RefreshControl
    refreshableTitle: null,
    refreshableColors: ['dimgray', 'tomato', 'limegreen'],
    refreshableProgressBackgroundColor: 'white',
    refreshableSize: undefined,
    refreshableTintColor: 'lightgray',
    customRefreshControl: null,

    // Pagination
    pagination: true,
    autoPagination: true,
    allLoadedText: '已经全部加载完毕',

    // Spinner
    spinnerColor: '#666',
    waitingSpinnerSize: 'small',
    waitingSpinnerText: '加载中...',
    pageErrorText: '加载失败, 点击重新加载',
    // Pagination Button

    //state text
    emptyViewText: '暂无数据',
    // GridView
    numColumns: 1,
    stickyHeaderIndex: -1,
    startPage: 1,

  };

  static propTypes = {
    initialNumToRender: PropTypes.number,
    horizontal: PropTypes.bool,

    firstLoader: PropTypes.bool,
    scrollEnabled: PropTypes.bool,
    onFetch: PropTypes.func,

    // Custom ListView
    header: PropTypes.func,

    paginationAllLoadedView: PropTypes.func,
    paginationWaitingView: PropTypes.func,
    //    emptyView: PropTypes.func,
    stateView: PropTypes.func,
    separator: PropTypes.func,
    //是否启用默认分割线
    enableDefaultSeparator: PropTypes.bool,
    defaultSeparatorColor: PropTypes.string,
    // Refreshable
    refreshable: PropTypes.bool,
    refreshableMode: PropTypes.string,

    // RefreshControl
    refreshableTitle: PropTypes.string,
    refreshableColors: PropTypes.array,
    refreshableProgressBackgroundColor: PropTypes.string,
    refreshableSize: PropTypes.string,
    refreshableTintColor: PropTypes.string,
    customRefreshControl: PropTypes.func,

    // Pagination
    pagination: PropTypes.bool,
    autoPagination: PropTypes.bool,
    allLoadedText: PropTypes.string,

    // Spinner
    spinnerColor: PropTypes.string,
    waitingSpinnerSize: PropTypes.any,
    waitingSpinnerText: PropTypes.string,
    pageErrorText: PropTypes.string,

    //stateText
    emptyViewText: PropTypes.string,
    // GridView
    numColumns: PropTypes.number,
    /**
     * How to render a row in a FlatList. Should return a valid React Element.
     */
    renderItem: PropTypes.func,

    stickyHeaderIndex: PropTypes.number,

    startPage: PropTypes.number,
    footer: PropTypes.func,
  };

  constructor(props) {
    super(props);
    this.setPage(props.startPage);
    this.rows = [];
    //swipe
    this.headerHeight = 0;


    this.state = {
      dataSource: [],
      isRefreshing: false,
      paginationStatus: PaginationStatus.firstLoad,
      error: '',
      listHeight: 0,
      listWidth: width,
      dummy: false,
    };
  }

  onRefresh = () => {
    if (this.mounted) {
      if (this.state.paginationStatus === PaginationStatus.firstLoad) {
        this.endFetch();
      } else {
        this.setState({
          isRefreshing: true,
        }, () => {
          this.setPage(this.props.startPage);
          this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
        });
      }
    }
  };

  onPaginate = () => {
    if (this.state.paginationStatus !== PaginationStatus.allLoaded && this.state.paginationStatus !== PaginationStatus.loading && !this.state.isRefreshing) {
      const rows = this.rows || [];
      this.setState({ paginationStatus: PaginationStatus.loading, dataSource: rows.slice() }, () => {
        this.props.onFetch(this.getPage() + 1, this.postPaginate, this.abortFetch);
      });
    }
  };

  onEndReached = () => {
    if (this.props.pagination && this.props.autoPagination && this.state.paginationStatus === PaginationStatus.waiting) {
      requestAnimationFrame(() => {
        this.onPaginate();
      });
    }
  };

  setPage = page => this.page = page;

  getPage = () => this.page;

  setRows = rows => this.rows = rows;

  getRows = () => this.rows;
  refresh = () => {
    this.onRefresh();
  };

  scrollToOffset = (option) => {
    this._flatList.scrollToOffset(option);
  };

  scrollToIndex = (option) => {
    this._flatList.scrollToIndex(option);
  };

  scrollToItem = (option) => {
    this._flatList.scrollToItem(option);
  };

  scrollToEnd = (option) => {
    this._flatList.scrollToEnd(option);
  };

  postRefresh = (rows = [], pageLimit) => {
    if (this.mounted) {
      let paginationStatus = PaginationStatus.waiting;
      if (rows.length < pageLimit) {
        paginationStatus = PaginationStatus.allLoaded;
      }
      this.updateRows(rows, paginationStatus, '');
    }
  };

  endFetch = () => {
    if (this.mounted) {
      if (this.props.refreshableMode === 'advanced') {
        if (this._flatList._listRef._scrollRef.onRefreshEnd) {
          this._flatList._listRef._scrollRef.onRefreshEnd();
        }
        if (this._mjrefresh) {
          this._mjrefresh.finishRefresh();
        }
        if (this.rc) {
          this.rc.finishRefresh();
        }
      }
    }
  };

  abortFetch = (error) => {
    this.updateRows(null, PaginationStatus.loadFailed, error);
  };

  postPaginate = (rows = [], pageLimit) => {
    this.setPage(this.getPage() + 1);
    let paginationStatus;
    const oldRows = this.rows || [];
    let mergedRows = oldRows.concat(rows);
    if (rows.length === 0 || rows.length < pageLimit) {
      paginationStatus = PaginationStatus.allLoaded;
    } else {
      paginationStatus = PaginationStatus.waiting;
    }

    this.updateRows(mergedRows, paginationStatus, '');
  };

  updateRows = (rows, paginationStatus, error) => {
    let dataSource = rows;
    if (rows) {
      if (rows.length === 0 && this.props.header) {
        this.setRows([1]);
        this.setState({
          dataSource: [1],
          dummy: true,
          isRefreshing: false,
          paginationStatus,
          error,
        });
      } else {
        this.setRows(rows);

        this.setState({
          dataSource: rows,
          dummy: false,
          isRefreshing: false,
          paginationStatus,
          error,
        });
      }
    } else {
      if (this.state.isRefreshing) {
        this.setRows(rows);
        this.setState({
          dataSource: [],
          dummy: false,
          isRefreshing: false,
          paginationStatus,
          error,
        });
      } else {
        const oldRows = this.rows || [];
        this.setState({
          dataSource: oldRows.slice(),
          dummy: false,
          isRefreshing: false,
          paginationStatus,
          error,
        });
      }
    }

    if (this.props.refreshableMode === 'advanced') {
      this.endFetch();
    }
  };


  paginationAllLoadedView = () => {
    if (this.state.dummy) {
      return null;
    }
    if (this.props.pagination) {
      if (this.props.paginationAllLoadedView) {
        return this.props.paginationAllLoadedView();
      }

      return (
          <View style={styles.paginationView}>
            <Text style={styles.allLoadedText}>
              {this.props.allLoadedText}
            </Text>
          </View>
      );
    }
    return this.props.footer && this.props.footer();
  };

  paginationErrorView = (paginateCallback) => {
    if (this.props.pagination) {
      if (this.props.autoPagination) {
        return (
            <TouchableWithoutFeedback onPress={() => paginateCallback()}>
              <View style={styles.paginationView}>
                <Text
                    style={[styles.paginationViewText, { marginLeft: 5 }]}
                >{this.props.pageErrorText}
                </Text>
              </View>
            </TouchableWithoutFeedback>
        );
      }
    }

    return this.props.footer && this.props.footer();
  };

  paginationWaitingView = (paginateCallback) => {
    if (this.props.pagination) {
      if (this.props.autoPagination) {
        if (this.props.paginationWaitingView) {
          return this.props.paginationWaitingView(paginateCallback);
        }

        return (
            <View style={styles.paginationView}>
              <ActivityIndicator color={this.props.spinnerColor} size={this.props.waitingSpinnerSize} />
              <Text
                  style={[styles.paginationViewText, { marginLeft: 5 }]}
              >{this.props.waitingSpinnerText}
              </Text>
            </View>
        );
      }
    }

    return this.props.footer && this.props.footer();
  };

  renderHeader = () => {
    if (this.props.header) {
      return <View style={{ width: this.state.listWidth }} onLayout={event => {
        this.headerHeight = event.nativeEvent.layout.height;
      }}>{this.props.header()}</View>;
    }
    return null;
  };

  renderSeparator = () => {
    if (this.props.separator) {
      if (this.props.numColumns > 1) {
        return null;
      }

      return this.props.separator();
    } else if (this.props.enableDefaultSeparator) {
      return <View style={{ height: 1, backgroundColor: this.props.defaultSeparatorColor || '#eeeeee', width: width }} />;
    }
    return null;
  };

  renderLoadingView = ()=>{
    return <View style={{
      justifyContent: 'center',
      alignItems: 'center',
      flexDirection: 'row',
      height: this.state.listHeight,
      width: this.state.listWidth,
    }}>
      <ActivityIndicator size={'small'} />
      <Text style={{
        fontWeight: '400',
        fontSize: 16,
        color: '#666666',
        marginLeft: 10,
      }}>{this.props.waitingSpinnerText}</Text>
    </View>;
  }

  renderErrorView = ()=>{
    return <TouchableWithoutFeedback onPress={() => {
      this.setState({ paginationStatus: PaginationStatus.firstLoad });
      this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
    }}><View style={{
      justifyContent: 'center',
      alignItems: 'center',
      height: this.state.listHeight,
      width: this.state.listWidth,
    }}><Text style={{
      fontWeight: '400',
      fontSize: 16,
      color: '#666666',
    }}>{this.state.error}</Text></View></TouchableWithoutFeedback>;
  }

  renderStateView = () => {
    //刚进入
    if (this.state.paginationStatus === PaginationStatus.firstLoad) {
      if (this.props.stateView) {
        const view = this.props.stateView(0, this.state.listHeight);
        return view || this.renderLoadingView();
      } else {
        return this.renderLoadingView();
      }
    }
    //第一次加载失败
    else if (this.state.paginationStatus === PaginationStatus.loadFailed) {
      if (this.props.stateView) {
        const view = this.props.stateView(2, this.state.error, this.refresh, this.state.listHeight);
        return view || this.renderErrorView();
      } else {
        return this.renderErrorView();
      }
    }
    //第一次加载无数据
    else if (this.state.paginationStatus !== PaginationStatus.firstLoad) {
      return this.renderEmptyView();
    }

    return null;
  };

  emptyView() {
    return <View style={{
      alignItems: 'center',
      justifyContent: 'center',
      height: Math.max(this.state.listHeight - this.headerHeight, this.state.listHeight / 2),
      width: this.state.listWidth,
    }}>
      <Text style={{
        fontWeight: '400',
        fontSize: 16,
        color: '#666666',
      }}>{this.props.emptyViewText}</Text>
    </View>;
  }

  renderEmptyView() {
    if (this.props.stateView) {
      const view = this.props.stateView(1, Math.max(this.state.listHeight - this.headerHeight, this.state.listHeight / 2));
      return view || this.emptyView();
    } else {
      return this.emptyView();
    }
  }

  renderFooter = () => {
    //有数据, 但是加载失败了
    if (this.props.pagination) {
      if (this.state.paginationStatus === PaginationStatus.loadFailed && this.rows && this.rows.length !== 0) {
        return this.paginationErrorView(this.onPaginate);
      } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === false) {
        return this.paginationWaitingView(this.onPaginate);
      } else if (this.state.paginationStatus === PaginationStatus.waiting && this.props.autoPagination === true) {
        return this.paginationWaitingView();
      } else if (this.state.paginationStatus === PaginationStatus.loading) {
        return this.paginationWaitingView();
      } else if (this.rows && this.rows.length !== 0 && this.state.paginationStatus === PaginationStatus.allLoaded) {
        return this.paginationAllLoadedView();
      }
    }
    if (this.props.footer) {
      return this.props.footer && this.props.footer();
    }
    return null;
  };

  renderScrollComponent = (props) => {

    if (this.props.refreshable) {

      if (this.props.refreshableMode === 'advanced') {
        if (Platform.OS === 'ios') {
          return <RefreshableScrollView
              {...props}
              style={{width:'100%', height: this.state.listHeight}}
              insideOfUltimateListView
              onRefresh={this.onRefresh}
              ref={ref => this.scrollView = ref}
          />;
        } else {
          return <ScrollView
              {...props}
              ref={ref => this.scrollView = ref}
              refreshControl={<SmartRefreshHeader
                  onRefresh={this.onRefresh}
                  ref={ref => this.rc = ref}
                  progressViewOffset={this.props.contentContainerStyle ? this.props.contentContainerStyle.paddingTop : 0}
              />}
              stickyHeaderIndices={this.props.stickyHeaderIndex >= 0 ? [this.props.stickyHeaderIndex + 1] : undefined}
          />;
        }
      } else {
        return (
            <ScrollView
                {...props}
                ref={ref => this.scrollView = ref}
                refreshControl={<RefreshControl
                    onRefresh={this.onRefresh}
                    refreshing={this.state.isRefreshing}
                    colors={this.props.refreshableColors}
                    progressBackgroundColor={this.props.refreshableProgressBackgroundColor}
                    size={this.props.refreshableSize}
                    tintColor={this.props.refreshableTintColor}
                    title={this.props.refreshableTitle}
                    progressViewOffset={this.props.contentContainerStyle ? this.props.contentContainerStyle.paddingTop : 0}
                />}
                stickyHeaderIndices={this.props.stickyHeaderIndex >= 0 ? [this.props.stickyHeaderIndex + 1] : undefined}
            />
        );
      }
    }

    return (
        <ScrollView
            {...props}
            ref={ref => this.scrollView = ref}
        />
    );
  };

  deleteRow = (item) => {
    let dataSource = this.state.dataSource.slice();
    const index = dataSource.indexOf(item);
    if (index >= 0) {
      dataSource.splice(index, 1);
      LayoutAnimation.easeInEaseOut();
      this.updateDataSource(dataSource);
    }
  };

  prependRow = (item) => {
    let dataSource = this.state.dataSource.slice();
    LayoutAnimation.easeInEaseOut();
    this.updateDataSource([item].concat(dataSource));
  }

  _keyExtractor = (item) => {
    if (typeof (item) === 'object') {
      return item.key !== undefined ? item.key : item;
    } else {
      return item;
    }
  };

  componentDidMount() {
    this.mounted = true;
    if (this.props.firstLoader) {
      this.props.onFetch(this.getPage(), this.postRefresh, this.abortFetch);
    }
  }

  componentWillUnmount() {
    this.mounted = false;
  }

  updateDataSource(rows = []) {
    this.setRows(rows);
    this.setState({
      dataSource: rows,
    });
  }

  dummyItem = () => {
    return this.renderEmptyView();
  };

  render() {
    const { numColumns } = this.props;
    const style = (this.rows && this.rows.length > 0) ? null : {
      height: this.state.listHeight,
      width: this.state.listWidth,
      alignItems: 'center',
    };
    return (
        <AnimatedFlatList
            onLayout={event => {
              this.setState({ listHeight: event.nativeEvent.layout.height, listWidth: event.nativeEvent.layout.width });
            }}
            onScroll={e => this.onScroll(e)}
            renderScrollComponent={this.renderScrollComponent}
            keyExtractor={this._keyExtractor}
            onEndReachedThreshold={0.2}
            contentContainerStyle={style}//fix emptyview center problem
            {...this.props}
            ref={ref => this.setRefs(ref)}
            data={this.state.dataSource}
            renderItem={(rowData) => this.renderItem(rowData)}
            ItemSeparatorComponent={this.renderSeparator}
            ListHeaderComponent={this.renderHeader}
            ListFooterComponent={this.renderFooter}
            ListEmptyComponent={this.renderStateView}
            onEndReached={this.onEndReached}
            numColumns={numColumns}
            key={numColumns}
        />
    );
  }

  setRefs(ref) {
    this._flatList = ref;
    this.props.listViewRef && this.props.listViewRef(ref);
  }

  setScrollEnabled(enable) {
    // Due to multiple issues reported across different versions of RN
    // We do this in the safest way possible...
    if (this._listView && this._listView.setNativeProps) {
      this._listView.setNativeProps({ scrollEnabled: enable });
    } else if (this._listView && this._listView.getScrollResponder) {
      const scrollResponder = this._listView.getScrollResponder();
      scrollResponder.setNativeProps && scrollResponder.setNativeProps({ scrollEnabled: enable });
    }
    this.props.onScrollEnabled && this.props.onScrollEnabled(enable);
  }


  // When deleting rows on iOS, the list may end up being over-scrolled,
  // which will prevent swiping any of the remaining rows. This triggers a scrollToEnd
  // when that happens, which will make sure the list is kept in bounds.

  onScroll(e) {
    if (Platform.OS === 'ios') {
      this.yScrollOffset = e.nativeEvent.contentOffset.y;
    }
    this.props.onScroll && this.props.onScroll(e);
  }

  onLayout(e) {
    this.layoutHeight = e.nativeEvent.layout.height;
    this.props.onLayout && this.props.onLayout(e);
  }

  renderCell(VisibleComponent, key, item) {
    return React.cloneElement(
        VisibleComponent,
        {
          ...VisibleComponent.props,
          setScrollEnabled: enable => this.setScrollEnabled(enable),
        },
    );
  }

  renderItem(rowData) {
    if (this.state.dummy) {
      return this.dummyItem();
    }
    else {
      let { item, index } = rowData;
      if (index < this.props.headerCount) {
        return this.props.headerAtIndex(index);
      } else {
        const Component = this.props.renderItem(rowData);
        let { key } = item;
        if (!key && this.props.keyExtractor) {
          key = this.props.keyExtractor(item, index);
        }
        return this.renderCell(Component, key, item);
      }
    }
  }
}

const styles = StyleSheet.create({
  fetchingView: {
    width: '100%',
    height,
    justifyContent: 'center',
    alignItems: 'center',
  },
  paginationView: {
    flex: 0,
    width: '100%',
    height: 55,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
  },
  paginationViewText: {
    fontSize: 16,
  },
  paginationViewSpinner: {
    marginRight: 5,
  },
  separator: {
    height: 0.5,
    marginLeft: 15,
    marginRight: 15,
    backgroundColor: 'lightgray',
  },

  allLoadedText: {
    alignSelf: 'center',
    color: '#bfbfbf',
  },
  gridItem: {
    overflow: 'hidden',
    alignItems: 'center',
    justifyContent: 'center',
  },
  gridView: {
    flexDirection: 'row',
    alignItems: 'flex-start',
    flexWrap: 'wrap',
  },
  listEmpty: {
    alignItems: 'center',
    justifyContent: 'center',
    flexGrow: 1,
    height: height,
  },
});
